package generator

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"encoding/binary"
	"errors"
	"fmt"
	"hash"
	"math"
	"time"

	"generatorserver/internal/gcs"

	"github.com/zeebo/xxh3"
)

const (
	keyLength = 14 // 随机生成的key长度
)

// 12+2+10+8+8 = 40
type License struct {
	Key    []byte     // 随机生成的key
	Secret CardSecret // 密钥值
	Expiry time.Time  // 过期时间
}

// 生成一个指定长度的随机字符串
func randomString(length int) []byte {
	randomBytes := make([]byte, length)
	_, err := rand.Read(randomBytes[:length-2])
	if err != nil {
		panic(err)
	}
	var sum byte = 0
	var xor byte = 0xf
	for i := 0; i < length-2; i++ {
		xor ^= randomBytes[i]
		sum += randomBytes[i]
	}
	randomBytes[length-2] = xor
	randomBytes[length-1] = sum
	return randomBytes
}

// 生成一组指定数量的License
func GenerateLicenses(serverId byte, numLicenses int) []License {
	// 定义一个空的License切片，用于存放生成的License
	licenses := make([]License, numLicenses)
	for i := 0; i < numLicenses; i++ {
		// 生成随机的key
		key := randomString(keyLength)

		// 生成过期时间（随机在当前时间基础上往后推至多90天）
		maxDays := 90
		days := int(math.Floor(randFloat() * float64(maxDays)))
		expiry := time.Now().AddDate(10, 0, days)

		// 生成License
		license := License{
			Key:    key,
			Secret: NewCardSecret(serverId),
			Expiry: expiry,
		}
		licenses[i] = license
	}
	return licenses
}

// 随机生成一个0到1之间的浮点数
func randFloat() float64 {
	b := make([]byte, 8)
	_, err := rand.Read(b)
	if err != nil {
		panic(err)
	}
	return float64(int64(binary.BigEndian.Uint64(b)&(^uint64(0)>>1))) / (1 << 63)
}

// 根据批次号和License Key生成License
func generateLicense(key []byte) *License {
	// 生成过期时间（随机在当前时间基础上往后推至多90天）
	maxDays := 90
	days := int(math.Floor(randFloat() * float64(maxDays)))
	expiry := time.Now().AddDate(10, 0, days)

	// 生成License
	license := License{
		Key:    key,
		Expiry: expiry,
	}
	return &license
}

// bcd算法

func decimalToBCD(decimal int) int {
	bcd := 0
	shift := 0
	for decimal > 0 {
		digit := decimal % 10
		bcd |= digit << shift
		decimal /= 10
		shift += 4
	}
	return bcd
}

func bcdToDecimal(bcd int) int {
	decimal := 0
	multiplier := 1
	for bcd > 0 {
		digit := bcd & 0xf
		decimal += digit * multiplier
		multiplier *= 10
		bcd >>= 4
	}
	return decimal
}

func xxh3Hash() hash.Hash {
	return xxh3.New()
}

func (lic *License) ToBytes() ([]byte, error) {
	// Create a new buffer to hold the license data
	buf := new(bytes.Buffer)

	// Write the feature flags to the buffer
	if err := binary.Write(buf, binary.LittleEndian, lic.Key); err != nil {
		return nil, err
	}

	// Write the expiration time to the buffer as a Unix timestamp
	if err := binary.Write(buf, binary.LittleEndian, lic.Expiry.Unix()); err != nil {
		return nil, err
	}

	// Write the expiration time to the buffer as a Unix timestamp
	if err := binary.Write(buf, binary.LittleEndian, lic.Secret); err != nil {
		return nil, err
	}

	xxh3H := hmac.New(xxh3Hash, []byte(gcs.AppName))
	xxh3H.Write(buf.Bytes())
	sig := xxh3H.Sum(nil)

	// Generate an ed25519 signature for the license data using the secret key
	// sig := ed25519.Sign(ed25519Cert.PrivateKey.(ed25519.PrivateKey), buf.Bytes())

	// Write the signature to the buffer
	if err := binary.Write(buf, binary.LittleEndian, sig); err != nil {
		return nil, err
	}

	// Return the contents of the buffer as a byte slice
	return buf.Bytes(), nil
}

func (lic *License) String() string {
	bs, err := lic.ToBytes()
	if err != nil {
		return ""
	}
	return b32.EncodeToString(bs)
}

var (
	errInvalidLicense    = errors.New("License is invalid.")
	errLicenseHasExpired = errors.New("License has expired")
)

func ParseLicense(src string) (*License, error) {
	if len(src) != 64 {
		return nil, fmt.Errorf("License len error.")
	}
	bs, err := b32.DecodeString(src)
	if err != nil {
		return nil, err
	}
	if len(bs) != 40 {
		return nil, fmt.Errorf("License len error.")
	}
	sig := bs[32:]
	xxh3H := hmac.New(xxh3Hash, []byte(gcs.AppName))
	xxh3H.Write(bs[:32])
	if !bytes.Equal(xxh3H.Sum(nil), sig) {
		return nil, errInvalidLicense
	}
	key := bs[:14]
	var sum byte = 0
	var xor byte = 0xf
	for i := 0; i < keyLength-2; i++ {
		xor ^= key[i]
		sum += key[i]
	}
	if key[12] != xor || key[13] != sum {
		return nil, errInvalidLicense
	}
	u64 := binary.LittleEndian.Uint64(bs[14:22])
	expiry := time.Unix(int64(u64), 0)
	if time.Since(expiry) > 0 {
		return nil, errLicenseHasExpired
	}
	cs, err := ParseCardSecretB(bs[22:32])
	if err != nil {
		return nil, errInvalidLicense
	}
	return &License{
		Key:    key,
		Expiry: expiry,
		Secret: cs,
	}, nil
}
